﻿namespace LiteBlog.Components
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using LiteBlog.Common.Entities;
    using LiteBlog.Components.Enums;
    using LiteBlog.Components.Contracts;
    using LiteBlog.Repositories.Contracts;
    using LiteBlog.Common.Components;

    public class CategoryComp : BaseComp<ICategoryRepository>, ICategoryComp
    {
        public CategoryComp(ICategoryRepository data, ICacheComp cache, IBlogRepository blogRepo, IPostRepository postRepo, ICategoryRepository categoryRepo, ISiteMapComp sitemapComp) : base(data, cache)
        {
            _sitemapComp = sitemapComp;
            _blogRepo = blogRepo;
            _postRepo = postRepo;
            _categoryRepo = categoryRepo;
        }

        private ISiteMapComp _sitemapComp;
        public ISiteMapComp SiteMapComponent
        {
            get
            {
                return _sitemapComp;
            }
        }

        public string GetNames(string csvIds)
        {
            string[] ids = csvIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            return ids.Select(id => Get(id).Name).Aggregate((cur, next) => cur + ", " + next);
        }

        public IEnumerable<Category> Get()
        {
            var categories = _cache.Get<IEnumerable<Category>>(CacheType.Categories);
            if (categories == null)
            {
                categories = _data.Get().OrderBy(cat => cat.Order).ToList();
                _cache.Put(CacheType.Categories, categories);
            }

            return categories;
        }

        public Category Get(string id)
        {
            return Get().SingleOrDefault(item => item.CatID.Equals(id));
        }

        public MethodResult Create(Category item)
        {
            if (IdExists(item.CatID))
                return MethodResult.DuplicateId;
            if (NameExists(item.Name))
                return MethodResult.DuplicateName;
            var methodResult = _data.Create(item) ? MethodResult.CreateSuccess : MethodResult.DuplicateId;
            
            SiteMapComponent.Add(SiteMapType.Category, GetSiteMapUrl(item.CatID));
            return methodResult;
        }

        public MethodResult Update(string olid, Category item)
        {
            var oldItem = Get(olid);
            if (oldItem == null)
                return MethodResult.Failure;

            if (oldItem.CatID != item.CatID)
                if (IdExists(item.CatID))
                    return MethodResult.DuplicateId;

            if (oldItem.Name != item.Name)
                if (NameExists(item.Name))
                    return MethodResult.DuplicateName;

            if (olid != item.CatID)
            {
                ChangeCategory(olid, item.CatID);
                SiteMapComponent.UpdateUrl(SiteMapType.Category, GetSiteMapUrl(olid).Location, GetSiteMapUrl(item.CatID).Location);
            }
            else
            {
                SiteMapComponent.Update(SiteMapType.Category, GetSiteMapUrl(item.CatID));
            }

            _data.Update(olid, item);
            return MethodResult.UpdateSuccess;
        }

        public MethodResult Delete(string id)
        {
            ChangeCategory(id, string.Empty);
            _data.Delete(id);
            SiteMapComponent.Delete(SiteMapType.Category, GetSiteMapUrl(id).Location);
            return MethodResult.DeleteSuccess;
        }

        private bool IdExists(string id)
        {
            var item = Get(id);
            return item != null;
        }

        private bool NameExists(string name)
        {
            var item = Get().SingleOrDefault(c => c.Name == name);
            return item != null;
        }

        
        private void ChangeCategory(string olid, string newid)
        {
            var postsToUpdate = new List<PostInfo>();
            var postInfos = _blogRepo.Get();
            foreach(var postInfo in postInfos)
            {
                var catIds = postInfo.Categories.Select(c => c.CatID);
                if(catIds.Contains(olid))
                {
                    postsToUpdate.Add(postInfo);
                }
            }

            if(postsToUpdate.Any())
            {
                foreach (var postToUpdate in postsToUpdate)
                {
                    var post = _postRepo.Load(postToUpdate.FileID);
                    var oldCategory = post.Categories.SingleOrDefault(c => c.CatID.Equals(olid));
                    if(oldCategory != null)
                    {
                        post.Categories.Remove(oldCategory);
                    }
                    if(!string.IsNullOrEmpty(newid))
                    {
                        var newCategory = _categoryRepo.Get().SingleOrDefault(c => c.CatID.Equals(newid));
                        if (newCategory != null)
                        {
                            post.Categories.Add(newCategory);
                        }
                    }
                    _postRepo.Save(post);
                }

                // change all posts, and then change the post metadata
                _blogRepo.ChangeCategory(olid, newid);
            }
        }

        private SiteMapUrl GetSiteMapUrl(string catId)
        {
            var siteMapUrl = new SiteMapUrl
            {
                Location = "/" + catId,
                LastModifiedTime = DateTime.UtcNow
            };
            return siteMapUrl;
        }

        private IBlogRepository _blogRepo;
        private IPostRepository _postRepo;
        private ICategoryRepository _categoryRepo;
    }
}